package cn.zxcoder.Tools;

import cn.zxcoder.entity.SystemManage.Menu;
import cn.zxcoder.entity.SystemManage.Right;
import cn.zxcoder.entity.pay.Cnf;
import cn.zxcoder.entity.pay.CnfInfo;
import cn.zxcoder.entity.pay.Mode;
import cn.zxcoder.entity.pay.Order;
import cn.zxcoder.service.ICnfInfoService;
import cn.zxcoder.service.IMenuService;
import cn.zxcoder.service.IModeService;
import cn.zxcoder.service.IRightService;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import redis.clients.jedis.Jedis;

import javax.servlet.http.HttpServletRequest;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.*;
import java.util.stream.Collectors;

import static java.util.stream.Collectors.toMap;

public class ToolUtil {

    public static Jedis jedis;


    static {
        jedis = new Jedis("192.168.127.4", 6379);
    }

    public static String simpleUUID() {
        return UUID.randomUUID().toString().replaceAll("-", "");
    }

    public static String getUrl(HttpServletRequest request) {
        return request.getRemoteAddr();
    }

    /**
     * 通过menu对象的schildren字符串来设置它的children属性
     * @param menu 菜单对象
     */
    public static void setMenuChildren(Menu menu, IMenuService menuServiceIml) {
        String schildren = menu.getSchildren();
        if (schildren != null && !schildren.equals("")) {
            String[] ms = schildren.split(",");
            List<Menu> menus = new ArrayList<>();
            for (String m : ms) {
                Menu me = menuServiceIml.getById(Integer.parseInt(m));
                menus.add(me);
            }
            menu.setChildren(menus);
        } else {
            menu.setChildren(null);
        }
    }

    /**
     * 因为service查询Menu表后会出现子菜单重复出现对的情况，所以用此方法删除多余的子菜单
     * @param list 查询后的数组
     */
    public static void removeRedundanceMenu(List<Menu> list, IMenuService menuServiceIml) {
        Iterator<Menu> iterator = list.iterator();
        List<Menu> menus = new ArrayList<>();

        while (iterator.hasNext()) {
            Menu menu = iterator.next();
            setMenuChildren(menu, menuServiceIml);
            if (menus.contains(menu)) {
                iterator.remove();
                continue;
            }
            List<Menu> children = menu.getChildren();
            if (children != null) {
                menus.addAll(children);
            }

        }
    }

    /**
     * 多表查询：给page数据中填充其他表查询的数据到相应的字段，组成一个完整的对象
     * @param page page的数据
     * @param tClass 被填充的对象的class类
     * @param methodName 被填充对象的外键属性的get方法
     * @param eClass 要填充数据实体类的service接口的class类
     * @param field service接口需要用到的数据库查询方法名称
     * @param iClass 要填充数据的实体类对象的class类
     * @param args 要填充哪些属性
     * @param <T> 被填充类
     * @param <E> service接口
     * @param <I> 填充的类
     */
    public static<T,E,I> void setFields(IPage<T> page, Class<T> tClass, String methodName,Class<I> eClass,String field,Class<E> iClass,String ...args) throws NoSuchMethodException, InvocationTargetException, IllegalAccessException, InstantiationException {
        String tMethodName = "get" + (char) (methodName.charAt(0) - 'a' + 'A') + methodName.substring(1);
        Method method = tClass.getMethod(tMethodName);
        Set<Integer> ids = page.getRecords().stream().map(a-> {//得到外键的id属性集合
            try {
                return (Integer)(method.invoke(a));
            } catch (Exception e) {
                e.printStackTrace();
                return null;
            }
        }).collect(Collectors.toSet());
        //通过这些id集合来查询相应的填充对象得到数组
        QueryWrapper<I> iWrapper = new QueryWrapper<>();
        iWrapper.in("id", ids);//这里表明设置外键的表的字段必须是id
        //获取service接口的方法
        Method eMethod = eClass.getMethod(field);
        //调用接口方法查询出填充对象集合
//        Object invoke = eMethod.invoke(eClass.newInstance(), iWrapper);
//        System.out.println("==============================================================");
//        System.out.println(invoke);
        List<I> entities = (List<I>) eMethod.invoke(eClass.newInstance(), iWrapper);
        Method getId = iClass.getMethod("getId");//这是它一定用到的id的get方法

        //获取所有要填充属性的get方法
        String[] methods = getMethodName(args);
        for (String method1 : methods) {
            Method method2 = iClass.getMethod(method1);
            //获取id->对应属性(默认为String类型)的map映射
            Map<Integer, String> map = entities.stream().collect(toMap(k -> {
                try {
                    return (Integer) getId.invoke(iClass);
                } catch (Exception e) {
                    e.printStackTrace();
                    return null;
                }
            }, v -> {
                try {
                    return(String) method2.invoke(iClass);
                } catch (Exception e) {
                    e.printStackTrace();
                }
                return null;
            }));
            //将page里的数据填充相应的属性
            page.convert(e -> {
                try {
                    method2.invoke(map.get(method.invoke(e)));
                } catch (IllegalAccessException | InvocationTargetException illegalAccessException) {
                    illegalAccessException.printStackTrace();
                }
                return null;
            });
        }
    }


    /**
     * 用于将字段变为get形式的字符串
     * @param args 字段数组
     * @return 变为方法名称的字符串数组
     */
    public static String[] getMethodName(String[] args) {
        String pre = "get";
        for (int i = 0; i < args.length; i++) {
            char c = (char) (args[i].charAt(0) - 'a' + 'A');
            args[i]=pre + c + args[i].substring(1);
        }
        return args;
    }


    /**
     * 多表查询的赋值操作
     * @param page1 page数据
     * @param iModeServiceIml 插入属性的类的service接口
     */
    public static void setFieldCnf(IPage<Cnf> page1, IModeService iModeServiceIml) {
            //获取所有配置信息的支付方式id
            Set<Integer> ids = page1.getRecords().stream().map(Cnf::getMode).collect(Collectors.toSet());
            //通过这些id来查询到相应的Mode支付方式对象数组
            QueryWrapper<Mode> modeQueryWrapper = new QueryWrapper<>();
            modeQueryWrapper.in("id", ids);
            List<Mode> modes = iModeServiceIml.list(modeQueryWrapper);
            //得到id->name的映射hashmap
            Map<Integer, String> map = modes.stream().collect(toMap(Mode::getId, Mode::getName));
            for (Cnf record : page1.getRecords()) {
                record.setName(map.get(record.getMode()));
            }
    }

    /**
     * 多表查询的赋值操作
     * @param page1 page数据
     * @param iModeServiceIml 插入属性的类的service接口
     */
    public static void setFieldCnfInfo(IPage<CnfInfo> page1, IModeService iModeServiceIml) {
        //获取所有配置信息的支付方式id
        Set<Integer> ids = page1.getRecords().stream().map(CnfInfo::getMode).collect(Collectors.toSet());
        //通过这些id来查询到相应的Mode支付方式对象数组
        QueryWrapper<Mode> modeQueryWrapper = new QueryWrapper<>();
        modeQueryWrapper.in("id", ids);
        List<Mode> modes = iModeServiceIml.list(modeQueryWrapper);
        //得到id->name的映射hashmap
        Map<Integer, String> map = modes.stream().collect(toMap(Mode::getId, Mode::getName));
        for (CnfInfo record : page1.getRecords()) {
            record.setName(map.get(record.getMode()));
        }
    }

    /**
     * 通过id来获取T对象集合中对应的T对象。
     * @param list T集合
     * @param tClass T的class对象
     * @param id id号
     * @param <T> 泛型
     * @return 对应id值的T对象t
     * @throws Exception
     */
    public static <T> T getRecord(List<T> list,Class<T> tClass, int id) throws Exception {
        try {
            Method getId = tClass.getMethod("getId");
            for (T t : list) {
                if ((int)getId.invoke(t) == id) {
                    return t;
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;

    }

    /**
     * 通过设置条件的wrapper获取父级权限对象，然后在schildren中加入id参数  (这里id是保存权限后获得的id)
     * @param iRightServiceIml 权限Right的service接口
     * @param id 该权限保存后的id值
     * @param rightQueryWrapper 获取父级权限节点的条件器
     * @return 应该设置的schildren字符串
     */
    public static String setSchildren(IRightService iRightServiceIml, Integer id, QueryWrapper<Right> rightQueryWrapper) {

        //查询父级节点对应的right权限对象的schildren值
        Right parent = iRightServiceIml.getOne(rightQueryWrapper);
        if (parent == null) {
            return null;
        }
        String _schildren = parent.getSchildren();

        //将把此id值加到原schildren中

        return _schildren==null ? "" + id : _schildren + "," + id;
    }

    /**
     * 根据获取的所有权限id集合，去把他们的一部分权限设置到对应权限的children中。最后做成用children表示子节点数组的树形结构
     * @param ids 权限id集合
     * @param iRightServiceIml 权限Right的service接口
     * @return 设置成树形结构后的集合数据
     */
    public static List<Right> setchildren(List<Integer> ids, IRightService iRightServiceIml) {
        ArrayList<Right> res = new ArrayList<>();
        Iterator<Integer> iterator = ids.iterator();

    //先处理顶级权限
        //查出所有的顶级权限对象加入到res，并在ids集合中删除它们所对应的id
        while (iterator.hasNext()) {
            Integer id = iterator.next();
            //获取此id对应的权限对象
            Right right = iRightServiceIml.getById(id);
            //判断此对象是否是顶级权限对象
            String[] split = right.getRightCode().split(":");
            if (split.length == 1) {//证明是顶级权限
                res.add(right);
                iterator.remove();
            }
        }
        if (ids.size() == 0) {
            return res;
        }
    //再处理二级权限
        for (Right right : res) {//遍历所有的顶级权限
            List<Right> children = new ArrayList<>();//顶级权限的二级权限集合

            String schildren = right.getSchildren();
            String[] childrenId = schildren.split(",");//此顶级权限应有的孩子节点权限id数组
            //将ids剩下的二级权限放入到顶级权限相应的children集合中，并删除此二级权限在ids中对应的id
            for (String s : childrenId) {
                Integer id = Integer.parseInt(s);
                if (ids.contains(id)) {
                    children.add(iRightServiceIml.getById(id));
                    ids.remove(id);
                }
                if (ids.size() == 0) {
                    right.setChildren(children);
                    return res;
                }

            }
            right.setChildren(children);


            //直接处理三级权限
            for (Right child : children) {
                List<Right> children1 = new ArrayList<>();//此二级权限的三级权限集合

                String schildren1 = child.getSchildren();
                if (schildren1 == null) {
                    continue;
                }
                String[] childrenId1 = schildren1.split(",");
                for (String s : childrenId1) {
                    Integer id = Integer.parseInt(s);
                    if (ids.contains(id)) {
                        children1.add(iRightServiceIml.getById(id));
                        ids.remove(id);
                    }
                    if (ids.size() == 0) {
                        child.setChildren(children1);
                        return res;
                    }
                }
                child.setChildren(children1);
            }
        }
        return res;//这里可以写工具类方法，我懒得写了
    }

    /**
     * 用于查询数据可视化的返回order的天剑构造求
     * @param dept 部门
     * @param mode 支付方式
     * @param iCnfInfoServiceIml 配置信息的接口
     * @return 条件构造器
     */
    public static QueryWrapper<Order> getWrapper(String dept, String mode, ICnfInfoService iCnfInfoServiceIml,String type,int day, int month,boolean flag) {
        //先查询销售数据和点击量
        QueryWrapper<Order> wrapper = new QueryWrapper<>();
        if (!dept.equals("")) {
            wrapper.eq("dept_id", Integer.parseInt(dept));
        }
        if (!mode.equals("")) {
            QueryWrapper<CnfInfo> cnfInfoQueryWrapper = new QueryWrapper<>();
            cnfInfoQueryWrapper.eq("mode", mode);
            Integer cnfinfoId = iCnfInfoServiceIml.getOne(cnfInfoQueryWrapper).getId();
            wrapper.eq("cnfinfo_id", cnfinfoId);
        }
        wrapper.select(Order.class, info -> !info.getColumn().equals("mode") && !info.getColumn().equals("status") &&
                !info.getColumn().equals("editTime") && !info.getColumn().equals("editUser") && !info.getColumn().equals("tnumber") &&
                !info.getColumn().equals("configId") && !info.getColumn().equals("name") && !info.getColumn().equals("subName"));

        if (flag) {//判断是否是支付方式统计
            if (type.equals("day")) {
                wrapper.eq("day(addTime)", day);
            } else if (type.equals("month")) {
                wrapper.eq("month(addTime)", month);
            }//条件器构造结束
        }

        return wrapper;
    }
}